package br.org.bertol.mestrado.engine.optimisation.pso;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;

import org.apache.log4j.Logger;

import br.org.bertol.mestrado.engine.Objective;
import br.org.bertol.mestrado.engine.optimisation.AbstractRepository;
import br.org.bertol.mestrado.engine.optimisation.entity.Particle;
import br.org.bertol.mestrado.engine.optimisation.moo.AdaptativeGrid;
import br.org.bertol.mestrado.engine.optimisation.moo.OrdenatePareto;
import br.org.bertol.mestrado.engine.optimisation.moo.ParetoDominace;
import br.org.bertol.mestrado.engine.optimisation.moo.ParetoMinMinDominace;

/**
 * Implemeta o conceito do reposit�rio de pareto.
 * @author contaqualquer
 */
public final class PsoRepository<T> extends AbstractRepository<Particle> {

    /***/
    private final transient AdaptativeGrid<T> adaptativeGrid;

    /**
     * @param verify
     *            Objeto verify
     * @param divisons
     *            Número de bisecções do grid
     * @param repositorySize
     *            Tamanho do repositório
     */
    public PsoRepository(final int divisons,
        final int repositorySize, final Objective[] objectives) {

        super(new ParetoMinMinDominace());

        adaptativeGrid = new AdaptativeGrid<T>(divisons,
                repositorySize, this, objectives);

    }

    /**
     * Inicia a fronteira de pareto.
     * @param list
     *            PArtículas iniciais
     * @throws Exception
     *             Caso erro
     */
    @SuppressWarnings("unchecked")
    public void startParetoBorder(final List<Particle> list) throws Exception {

        final ArrayList<Particle> start = new ArrayList<Particle>(list);

        Collections.sort(start, new OrdenatePareto<Particle>(
                new ParetoMinMinDominace()));

        final ArrayList<Particle> tmp = new ArrayList<Particle>();

        for (Particle u : start) {
            tmp.add((Particle) u.clone());
        }

        tmp.trimToSize();

        adaptativeGrid.updateGrid((List<Particle>) tmp.clone());

        adaptativeGrid.updateGridStatus();
    }

    /**
     * Adiciona unidade no repositório.
     * @param unitList
     *            Lista de unidade
     * @throws Exception
     *             Caso ocorra algum erro durante a inserção
     */
    public void addInto(final List<Particle> unitList) throws Exception {

        adaptativeGrid.updateGrid(unitList);
    }

    /**
     * Retorna o Gbest escolhido por roleta.
     * @return Melhor objeto selecionado
     * @throws CloneNotSupportedException
     *             Erro ao clonar objeto gbest.
     */
    public Particle getGbest() throws CloneNotSupportedException {
        adaptativeGrid.updateGridStatus();

        return (Particle) adaptativeGrid.getUnit().clone();
    }

    /**
     * {@inheritDoc}
     */
    public int verifyDominance(final Particle unit) {

        final ArrayList<Particle> tmpRepository = new ArrayList<Particle>(0);

        tmpRepository.clear();

        boolean dominance = false;

        // verifica se este array domina alguem, não domina niguém ou é
        // ou é dominado por alguem
        for (Particle ndParticle : adaptativeGrid.getGrid()) {

            final int paretoDominante = paretoDominace.compare(unit
                    .getHeight(), ndParticle.getHeight());

            if (paretoDominante == ParetoDominace.RIGHT_DOMINE) {
                return ParetoDominace.RIGHT_DOMINE;
            }

            // caso domine alguma solução
            if (paretoDominante == ParetoDominace.LEFT_DOMINE
                    && !dominance) {

                tmpRepository.add(ndParticle);

                dominance = true;
            } else if (paretoDominante == ParetoDominace.LEFT_DOMINE) {
                tmpRepository.add(ndParticle);
            }
        }

        // caso tenha ocorrido alguma dominância
        // array temporário será maior que zero
        if (dominance) {

            Logger.getLogger(getClass())
                    .debug(
                           "Unidade candidata encontrada ----------------- : "
                    + unit);

            Logger.getLogger(getClass()).debug("Atualizando respositório.....");

            for (Particle dParticle : tmpRepository) {

                Logger
                        .getLogger(getClass())
                        .debug(
                               "Removendo unidade ---------------------------- : "
                                       + dParticle);

                adaptativeGrid.getGridPopulation()[dParticle
                        .getSpacialPosition()]--;

                Logger.getLogger(getClass())
                        .debug(
                               "Nova densidade populacional "
                        + adaptativeGrid.getGridPopulation()[dParticle
                        .getSpacialPosition()]);
            }
            adaptativeGrid.getGrid().removeAll(tmpRepository);

            Logger.getLogger(getClass()).debug("Respositório atualizado.");

            return ParetoDominace.LEFT_DOMINE;
        }

        // retorna o valor encontrado para esta comparação
        return ParetoDominace.LEFT_DONT_DOMINE;
    }

    /**
     * Retorna itens da lista pareto.
     * @return lista de soluções não dominadas
     */
    @SuppressWarnings("unchecked")
    public List<Particle> getParetoBorder() {
        return (List<Particle>) adaptativeGrid.getGrid().clone();
    }
}
